﻿#include "net_package.h"
#include "as_scopegrard.h"


std::string as::tools::compress(const std::string_view &data)
{
	z_stream stream;
	stream.zalloc = Z_NULL;
	stream.zfree = Z_NULL;
	stream.opaque = Z_NULL;

	if (deflateInit(&stream, Z_DEFAULT_COMPRESSION) != Z_OK) {
		return "";
	}

	stream.next_in = (Bytef *)data.data();
	stream.avail_in = data.size();

	constexpr int chunkSize = 4096;
	char out[chunkSize];
	std::string compressed;

	do {
		stream.next_out = (Bytef *)out;
		stream.avail_out = chunkSize;

		int ret = deflate(&stream, Z_FINISH);
		if (ret == Z_STREAM_ERROR) {
			deflateEnd(&stream);
			return "";
		}

		compressed.append(out, chunkSize - stream.avail_out);
	} while (stream.avail_out == 0);

	deflateEnd(&stream);
	return compressed;
}

std::string as::tools::decompress(const std::string_view &compressedData)
{
	z_stream stream;
	stream.zalloc = Z_NULL;
	stream.zfree = Z_NULL;
	stream.opaque = Z_NULL;

	if (inflateInit(&stream) != Z_OK) {
		return "";
	}

	stream.next_in = (Bytef *)compressedData.data();
	stream.avail_in = compressedData.size();

	constexpr int chunkSize = 4096;
	char out[chunkSize];
	std::string decompressed;

	do {
		stream.next_out = (Bytef *)out;
		stream.avail_out = chunkSize;

		int ret = inflate(&stream, Z_NO_FLUSH);
		if (ret == Z_STREAM_ERROR) {
			inflateEnd(&stream);
			return "";
		}

		decompressed.append(out, chunkSize - stream.avail_out);
	} while (stream.avail_out == 0);

	inflateEnd(&stream);
	return decompressed;
}

void as::tools::NetPacketHandle::Pack(ByteBuffer *in, ByteBuffer *out)
{
	auto write_pos = out->GetWritePos();
	as::tools::scope_guard buf_guard = [out, write_pos]()
		{
			out->SetWritePos(write_pos);
		};

	out->reserve(sizeof(uint8) * 2 + sizeof(uint16) + in->ReadableBytes());
	out->write_uint8(0x02);
	out->write_uint16(in->ReadableBytes());

	std::string_view data((char*)in->GetReadPtr(), in->ReadableBytes());
	z_stream stream;
	stream.zalloc = Z_NULL;
	stream.zfree = Z_NULL;
	stream.opaque = Z_NULL;

	if (deflateInit(&stream, Z_DEFAULT_COMPRESSION) != Z_OK) {
		return;
	}

	stream.next_in = (Bytef *)data.data();
	stream.avail_in = data.size();

	constexpr int chunkSize = 4096;
	char tmp[chunkSize];
	do {
		stream.next_out = (Bytef *)tmp;
		stream.avail_out = chunkSize;

		int ret = deflate(&stream, Z_FINISH);
		if (ret == Z_STREAM_ERROR) {
			deflateEnd(&stream);
			return;
		}

		out->write_bytes((uint8*)tmp, chunkSize - stream.avail_out);
	} while (stream.avail_out == 0);

	out->write_uint8(0x03);
	deflateEnd(&stream);
}

as::tools::PacketErr as::tools::NetPacketHandle::Unpack(ByteBuffer *in, ByteBuffer *out, int size)
{
	auto head_pos = in->GetReadPos();
	{
		uint8 head = 0;
		if (in->CanRead(sizeof(uint8)) == false)
			return PacketErr::PE_HEAD_TO_SMALL;

		in->read_obj(head);
		if (head != 0x02)
		{
			return PacketErr::PE_HEAD_ERROR;
		}
	}

	{
		uint16 body_len = 0;
		if (in->CanRead(sizeof(uint16)) == false)
		{
			in->SetReadPos(head_pos);
			return PacketErr::PE_BODY_TOO_SMALL;
		}

		in->read_obj(body_len);
		if (body_len > 10240 * 5)
		{
			return PacketErr::PE_BODY_TOO_BIG;
		}

		if (in->CanRead(body_len + sizeof(uint8)) == false)
		{
			in->SetReadPos(head_pos);
			return PacketErr::PE_BODY_TOO_SMALL;
		}

		uint8 *end = in->GetReadPtr() + body_len;
		if (*end != 0x03)
		{
			return PacketErr::PE_CHECK_ERR;
		}

		out->write_bytes(in->GetReadPtr(), body_len);
		in->AddReadPos(body_len + sizeof(uint8));
	}

	return PacketErr::PE_SUCCESS;
}


